﻿#pragma once
#include "template_vector.h"

template<typename T>
class TemplateMultiVector
{
public:

    TemplateMultiVector();

    TemplateMultiVector(int countRows, int countCols); // создается пустая матрица минимум 1х1, иначе ОШИБКА

    TemplateMultiVector(const TemplateMultiVector<T>& other); // копирует одну матрицу в другую

    const T& at(int row, int col) const; // возвращает значение в матрице

    void pushBackRow(const TemplateVector<T>& vec); // добавляет вектор в последнюю строку, вектор должен совпадать по размеру со строкой матрицы

    void pushBackCol(const TemplateVector<T>& vec); // добавляет вектор в последний столбец, вектор должен совпадать по размеру со строкой матрицы

    void removeLastRow(); // удаляет последнюю строку в матрице. Матрица дб не < 1х1 - иначе ошибка

    void removeLastCol(); // удаляетя последний столбец в матрице. Матрица дб не < 1х1 - иначе ошибка

    void print();

    void resize(int rowsCount, int colsCount); // изменяет размер матрицы, (0,0) обнуляет матрицу, 
    //(0,x) или (x,0) - не допускается

    void insertRow(int indexRow, const TemplateVector<T>& vec); // вставляет вектор по индексу в строку, но вектор должен быть размером со строку иначе ОШИБКА

    void insertCol(int indexCol, const TemplateVector<T>& vec); // вставляет вектор по индексу в столбец, но вектор должен быть размером со строку иначе ОШИБКА

    void removeRow(int indexRow); // удаляет строку по индексу, если после удаления матрица будет менньше (1.X) - ОШИБКА

    void removeCol(int indexCol); // удаляет столбец по индексу, если после удаления матрица будет менньше (X.1) - ОШИБКА

    const TemplateMultiVector<T>& operator=(const TemplateMultiVector<T>& other);

    const TemplateVector<T>& operator[](int indexRow) const; // возвращает определнную строку в матрице

    TemplateVector<T>& operator[](int indexRow); // возвращает определнную строку в матрице

    int rowsCount() const;

    int colsCount() const;


private:

    TemplateVector<TemplateVector<T>> _rowsData;

    void resizeRows(int newCountRows);

    void resizeСols(int newCountCols);
};


template<typename T>
inline TemplateMultiVector<T>::TemplateMultiVector()
{
}


template<typename T>
inline TemplateMultiVector<T>::TemplateMultiVector(int countRows, int countCols)
{
    assert(countCols > 0);

    _rowsData.resize(countRows);

    for (int i = 0; i < _rowsData.size(); i++)
        _rowsData[i].resize(countCols);
}


template<typename T>
inline TemplateMultiVector<T>::TemplateMultiVector(const TemplateMultiVector<T>& other) : _rowsData(other._rowsData)
{
}


template<typename T>
inline const T& TemplateMultiVector<T>::at(int row, int col) const
{
    return _rowsData[row].at(col);
}


template<typename T>
inline void TemplateMultiVector<T>::pushBackRow(const TemplateVector<T>& vec)
{
    assert(_rowsData.size() > 0 //проверяем пустая ли матрица, 
           && vec.size() > 0 // проверяем пустой ли вставляемый вектор,
           && vec.size() == this->colsCount()); //если не пустая, то проверяем == длины матрицы с длинной вектора

    _rowsData.pushBack(vec);
}


template<typename T>
inline void TemplateMultiVector<T>::pushBackCol(const TemplateVector<T>& vec)
{
    assert(_rowsData.size() > 0 && vec.size() == _rowsData.size()); // проверяем пустая ли матрица, 
    //если не пустая, то проверяем схожесть высоты матрицы с длинной вектора

    for (int i = 0; i < vec.size(); i++) // вставляем каждое зачение вектора в каждую строку и определный (последний) столбец матрицы
        _rowsData[i].pushBack(vec[i]);

    _rowsData.setSize(vec.size());
}


template<typename T>
inline void TemplateMultiVector<T>::removeLastRow()
{
    _rowsData.removeLast();
}


template<typename T>
inline void TemplateMultiVector<T>::removeLastCol()
{
    assert(_rowsData.size() > 0 && _rowsData[0].size() > 0);

    for (int i = 0; i < _rowsData.size(); i++)
        _rowsData[i].removeLast();
}


template<typename T>
inline void TemplateMultiVector<T>::print()
{
    if (_rowsData.size() == 0)
    {
        std::cout << "empty MultiVector" << std::endl << std::endl;

        return;
    }
        
    for (int i = 0; i < _rowsData.size(); ++i)
        _rowsData[i].print();

    std::cout << std::endl << std::endl<<std::endl;
}


template<typename T>
inline void TemplateMultiVector<T>::resize(int rowsCount, int colsCount)
{
    assert((rowsCount >= 0 && colsCount >= 0) &&
        (rowsCount != 0 && colsCount != 1) &&
        (rowsCount != 1 && colsCount != 0));
    //проверяем, чтобы количество строк и колонок были > или = 0 и проверяет чтобы не было
    //ситуации матрицы (X,0) или (0,X)
    //(0,0) допустимо

    _rowsData.resize(rowsCount);
    
    if (rowsCount != 0) // если количество строк 0, то и нет смысла столбцы менять
    {
        for (int i = 0; i < rowsCount; i++)
            _rowsData[i].resize(colsCount);
    }
}


template<typename T>
inline void TemplateMultiVector<T>::insertRow(int row, const TemplateVector<T>& vec)
{
    assert(vec.size() == this->colsCount()); // проверяем, чтобы длинна вставляемого вектора совпадала с длинной строки в матрице

    int tempSize = _rowsData.size();

    _rowsData.insert(row, vec);

    if (row > tempSize)
    {
        for (int i = tempSize; i < row; i++)        // это мы делаем из-за того, что когда индекс строки, в которую мы вставляем новую строку больше количества строк в матрице,                                            
            _rowsData[i].resize(this->colsCount()); // то элементы между старой матрицей и новой стройкой пустые
                                                    // короче заполняем строки между матрицей и новой строкой
    }
}


template<typename T>
inline void TemplateMultiVector<T>::insertCol(int index, const TemplateVector<T>& vec)
{
    assert(_rowsData.size() > 0 && vec.size() == _rowsData.size());

    for (int i = 0; i < _rowsData.size(); i++)
        _rowsData[i].insert(index, vec[i]);
}


template<typename T>
inline void TemplateMultiVector<T>::removeRow(int indexRow)
{
    _rowsData.remove(indexRow);
}


template<typename T>
inline void TemplateMultiVector<T>::removeCol(int indexCol)
{
    assert(_rowsData.size() > 0 && indexCol >= 0 && indexCol < this->colsCount());

    for (int i = 0; i < _rowsData.size(); i++)
        _rowsData[i].remove(indexCol);
}


template<typename T>
inline const TemplateMultiVector<T>& TemplateMultiVector<T>::operator=(const TemplateMultiVector<T>& other)
{
    _rowsData = other._rowsData;
    
    return *this;
}


template<typename T>
inline const TemplateVector<T>& TemplateMultiVector<T>::operator[](int index) const
{
    return _rowsData[index];
}


template<typename T>
inline TemplateVector<T>& TemplateMultiVector<T>::operator[](int index)
{
    return _rowsData[index];
}


template<typename T>
inline int TemplateMultiVector<T>::rowsCount() const
{
    return _rowsData.size();
}


template<typename T>
inline int TemplateMultiVector<T>::colsCount() const
{
    return _rowsData.size() == 0 ? 0 : _rowsData[0].size();
}


template<typename T>
inline void TemplateMultiVector<T>::resizeRows(int newCountRows)
{
    _rowsData.resize(newCountRows);
}

template<typename T>
inline void TemplateMultiVector<T>::resizeСols(int newCountCols)
{
    for (int i = 0; i < _rowsData.size(); i++)
        _rowsData[i].resize(newCountCols);
}